﻿using System;
using System.Design;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design.Behavior;
using System.CodeDom;
using System.ComponentModel.Design.Serialization;

// TODO: Move whole InfFront.InfoSources.Base.Design to be a separate assembly

namespace InfFront.InfoSources.Base.Design
{
    // TODO: implement footer panel here for "regular" components presentation and editing

    public abstract class BaseRootDesigner : System.Windows.Forms.Design.ComponentDocumentDesigner, IRootDesigner
    {
        private IComponentChangeService changeService;
        private ISelectionService selectionService;
        private IDesignerHost designerHost;
        private BaseDesignerControl mainControl;

        public BaseRootDesigner()
        {
        }

        // This method is where the designer initializes its state when
        // it is created.
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);

            // Connect to various designer services.
            InitializeServices();

            if (ChangeService != null)
            {
                ChangeService.ComponentChanged += OnAComponentChanged;
                ChangeService.ComponentRename += OnAComponentRename;
                ChangeService.ComponentAdded += OnComponentAdded;
                ChangeService.ComponentRemoved += OnComponentRemoved;
            }
        }

        // This utility method connects the designer to various services.
        // These references are cached for convenience.
        private void InitializeServices()
        {
            changeService = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
            selectionService = GetService(typeof(ISelectionService)) as ISelectionService;
            designerHost = GetService(typeof(IDesignerHost)) as IDesignerHost;
        }

        protected override void Dispose(bool disposing)
        {
            if (mainControl != null)
                mainControl.Dispose();
            if (ChangeService != null)
            {
                ChangeService.ComponentChanged -= OnAComponentChanged;
                ChangeService.ComponentRename -= OnAComponentRename;
                ChangeService.ComponentAdded -= OnComponentAdded;
                ChangeService.ComponentRemoved -= OnComponentRemoved;
            }
            base.Dispose(disposing);
        }

        public bool Selected
        {
            get 
            { 
                return SelectionService.PrimarySelection == Component; 
            }
            set
            {
                if (Selected == value)
                    return;
                SelectionService.SetSelectedComponents(new object[] { value? Component: DesignerHost.RootComponent }, SelectionTypes.Replace);
            }
        }

        protected IComponentChangeService ChangeService
        {
            get { return changeService; }
        }

        protected ISelectionService SelectionService
        {
            get { return selectionService; }
        }

        protected IDesignerHost DesignerHost
        {
            get { return designerHost; }
        }

        protected BaseDesignerControl MainControl
        {
            get
            {
                if (mainControl == null)
                {
                    mainControl = CreateControl();
                    mainControl.OnEnter(this, EventArgs.Empty);
                    mainControl.SetTrayControl(base.Control);
                }
                return mainControl;
            }
        }

        protected virtual void OnComponentRemoved(object sender, ComponentEventArgs e)
        {
        }

        protected virtual void OnComponentAdded(object sender, ComponentEventArgs e)
        {
        }

        protected virtual void OnAComponentChanged(object sender, ComponentChangedEventArgs e)
        {
        }

        protected virtual void OnAComponentRename(object sender, ComponentRenameEventArgs e)
        {
        }

        protected abstract BaseDesignerControl CreateControl();

        #region IRootDesigner Members

        object IRootDesigner.GetView(ViewTechnology technology)
        {
            switch (technology)
            {
                case ViewTechnology.Default:
                    return MainControl;
                default:
                    throw new NotSupportedException("The *technology* value is not supported");
            }
        }

        ViewTechnology[] IRootDesigner.SupportedTechnologies
        {
            get { return new ViewTechnology[] { ViewTechnology.Default }; }
        }

        #endregion
    }

    public class BaseRootCodeDomSerializer : TypeCodeDomSerializer
    {
        private static object INIT_METHOD_KEY = new object();

        protected override CodeMemberMethod GetInitializeMethod(IDesignerSerializationManager manager, CodeTypeDeclaration typeDecl, object value)
        {
            var method = typeDecl.UserData[INIT_METHOD_KEY] as CodeMemberMethod;
            if (method == null)
            {
                method = new CodeMemberMethod();
                method.Name = "InitializeComponent";
                method.Attributes = MemberAttributes.Private;
                typeDecl.UserData[INIT_METHOD_KEY] = method;
                var constructor = new CodeConstructor();
                constructor.Statements.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InitializeComponent", new CodeExpression[0]));
                typeDecl.Members.Add(constructor);
            }
            return method;
        }

        protected override CodeMemberMethod[] GetInitializeMethods(IDesignerSerializationManager manager, CodeTypeDeclaration typeDecl)
        {
            foreach (CodeTypeMember member in typeDecl.Members)
            {
                var method = member as CodeMemberMethod;
                if (((method != null) && method.Name.Equals("InitializeComponent")) && (method.Parameters.Count == 0))
                    return new CodeMemberMethod[] { method };
            }
            return new CodeMemberMethod[0];
        }

        private class DesignerSerializationManagerProxy : IDesignerSerializationManager
        {
            private IDesignerSerializationManager o;

            public DesignerSerializationManagerProxy(IDesignerSerializationManager origin)
            {
                this.o = origin;
            }

            #region IDesignerSerializationManager Members

            public void AddSerializationProvider(IDesignerSerializationProvider provider)
            {
                o.AddSerializationProvider(provider);
            }

            public ContextStack Context
            {
                get { return o.Context; }
            }

            public object CreateInstance(Type type, System.Collections.ICollection arguments, string name, bool addToContainer)
            {
                return o.CreateInstance(type, arguments, name, addToContainer);
            }

            public object GetInstance(string name)
            {
                return o.GetInstance(name);
            }

            public string GetName(object value)
            {
                return o.GetName(value);
            }

            public object GetSerializer(Type objectType, Type serializerType)
            {
                var rv = o.GetSerializer(objectType, serializerType);
                return rv;
            }

            public Type GetType(string typeName)
            {
                return o.GetType(typeName);
            }

            public PropertyDescriptorCollection Properties
            {
                get { return o.Properties; }
            }

            public void RemoveSerializationProvider(IDesignerSerializationProvider provider)
            {
                o.RemoveSerializationProvider(provider);
            }

            public void ReportError(object errorInformation)
            {
                o.ReportError(errorInformation);
            }

            public event ResolveNameEventHandler ResolveName
            {
                add { o.ResolveName += value; }
                remove { o.ResolveName -= value; }
            }

            public event EventHandler SerializationComplete
            {
                add { o.SerializationComplete += value; }
                remove { o.SerializationComplete -= value; }
            }

            public void SetName(object instance, string name)
            {
                o.SetName(instance, name);
            }

            #endregion

            #region IServiceProvider Members

            public object GetService(Type serviceType)
            {
                return o.GetService(serviceType);
            }

            #endregion
        }

        public override CodeTypeDeclaration Serialize(IDesignerSerializationManager manager, object root, System.Collections.ICollection members)
        {
            return base.Serialize(new DesignerSerializationManagerProxy(manager), root, members);
        }
    }
}